package com.jfireframework.codejson.function;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jfireframework.baseutil.collection.StringCache;
import com.jfireframework.baseutil.reflect.ReflectUtil;
import com.jfireframework.baseutil.smc.SmcHelper;
import com.jfireframework.baseutil.smc.compiler.JavaStringCompiler;
import com.jfireframework.baseutil.smc.model.CompilerModel;
import com.jfireframework.baseutil.smc.model.FieldModel;
import com.jfireframework.baseutil.smc.model.MethodModel;
import com.jfireframework.codejson.JsonArray;
import com.jfireframework.codejson.JsonObject;
import com.jfireframework.codejson.annotation.JsonIgnore;
import com.jfireframework.codejson.function.impl.read.DateReader;
import com.jfireframework.codejson.function.impl.read.FileReader;
import com.jfireframework.codejson.function.impl.read.ObjectReader;
import com.jfireframework.codejson.function.impl.read.array.BooleanArrayReader;
import com.jfireframework.codejson.function.impl.read.array.ByteArrayReader;
import com.jfireframework.codejson.function.impl.read.array.CharArrayReader;
import com.jfireframework.codejson.function.impl.read.array.DoubleArrayReader;
import com.jfireframework.codejson.function.impl.read.array.FloatArrayReader;
import com.jfireframework.codejson.function.impl.read.array.IntArrayReader;
import com.jfireframework.codejson.function.impl.read.array.LongArrayReader;
import com.jfireframework.codejson.function.impl.read.array.ShortArrayReader;
import com.jfireframework.codejson.function.impl.read.array.StringArrayReader;
import com.jfireframework.codejson.function.impl.read.wrapper.BigDecimalReader;
import com.jfireframework.codejson.function.impl.read.wrapper.BooleanReader;
import com.jfireframework.codejson.function.impl.read.wrapper.ByteReader;
import com.jfireframework.codejson.function.impl.read.wrapper.CharacterReader;
import com.jfireframework.codejson.function.impl.read.wrapper.DoubleReader;
import com.jfireframework.codejson.function.impl.read.wrapper.FloatReader;
import com.jfireframework.codejson.function.impl.read.wrapper.IntegerReader;
import com.jfireframework.codejson.function.impl.read.wrapper.LongReader;
import com.jfireframework.codejson.function.impl.read.wrapper.ShortReader;
import com.jfireframework.codejson.function.impl.read.wrapper.StringReader;
import com.jfireframework.codejson.methodinfo.MethodInfoBuilder;
import com.jfireframework.codejson.methodinfo.ReadMethodInfo;
import com.jfireframework.codejson.util.MethodComparator;
import com.jfireframework.codejson.util.NameTool;

public class ReaderContext
{
	private static ConcurrentMap<Type, JsonReader>	readerMap	= new ConcurrentHashMap<Type, JsonReader>();
	private static Set<Class<?>>					wrapperSet	= new HashSet<Class<?>>();
	private static final Logger						logger		= LoggerFactory.getLogger(ReaderContext.class);
	private static final AtomicInteger				count		= new AtomicInteger(1);
	
	static
	{
		readerMap.put(Boolean.class, new BooleanReader());
		readerMap.put(Byte.class, new ByteReader());
		readerMap.put(Character.class, new CharacterReader());
		readerMap.put(Double.class, new DoubleReader());
		readerMap.put(Float.class, new FloatReader());
		readerMap.put(Integer.class, new IntegerReader());
		readerMap.put(Long.class, new LongReader());
		readerMap.put(Short.class, new ShortReader());
		readerMap.put(String.class, new StringReader());
		readerMap.put(int[].class, new IntArrayReader());
		readerMap.put(byte[].class, new ByteArrayReader());
		readerMap.put(boolean[].class, new BooleanArrayReader());
		readerMap.put(char[].class, new CharArrayReader());
		readerMap.put(long[].class, new LongArrayReader());
		readerMap.put(short[].class, new ShortArrayReader());
		readerMap.put(float[].class, new FloatArrayReader());
		readerMap.put(double[].class, new DoubleArrayReader());
		readerMap.put(String[].class, new StringArrayReader());
		readerMap.put(Object.class, new ObjectReader());
		readerMap.put(Date.class, new DateReader());
		readerMap.put(File.class, new FileReader());
		readerMap.put(BigDecimal.class, new BigDecimalReader());
	}
	
	public static Object read(Type entityType, Object value)
	{
		return getReader(entityType).read(entityType, value);
	}
	
	public static JsonReader getReader(Type ckass)
	{
		JsonReader reader = readerMap.get(ckass);
		if (reader == null)
		{
			try
			{
				reader = (JsonReader) createReader(ckass, null).newInstance();
			}
			catch (Exception e)
			{
				throw new RuntimeException(e);
			}
			readerMap.put(ckass, reader);
		}
		return reader;
	}
	
	protected static JsonReader getReader(Type ckass, ReadStrategy readStrategy)
	{
		try
		{
			return (JsonReader) createReader(ckass, readStrategy).getConstructor(ReadStrategy.class).newInstance(readStrategy);
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 创建一个输出类cklas的jsonwriter
	 * 
	 * @param type
	 * @return
	 */
	protected static Class<?> createReader(Type type, ReadStrategy strategy)
	{
		String body = null;
		if (type instanceof Class)
		{
			Class<?> tmp = (Class<?>) type;
			if (tmp.isArray())
			{
				body = buildArrayReader(tmp);
			}
			else
			{
				StringCache stringCache = new StringCache();
				stringCache.append("{\r\n" + SmcHelper.getTypeName(tmp) + " entity = new " + SmcHelper.getTypeName(tmp) + "();\r\n");
				stringCache.append("JsonObject json = (JsonObject)$1;\r\n");
				Method[] methods = ReflectUtil.listSetMethod(tmp);
				Arrays.sort(methods, new MethodComparator());
				for (Method each : methods)
				{
					if (needIgnore(each, strategy))
					{
						continue;
					}
					ReadMethodInfo methodInfo = MethodInfoBuilder.buildReadMethodInfo(each, strategy);
					stringCache.append(methodInfo.getOutput());
				}
				stringCache.append("return entity;\r\n}");
				body = stringCache.toString();
			}
			return buildClass(body, tmp, strategy);
		}
		else if (type instanceof ParameterizedType)
		{
			Class<?> targetClass = (Class<?>) ((ParameterizedType) type).getRawType();
			if (targetClass.isInterface() || Modifier.isAbstract(targetClass.getModifiers()))
			{
				throw new RuntimeException("反序列化需要具体的类信息，不能是接口或者是抽象类");
			}
			else if (Collection.class.isAssignableFrom(targetClass))
			{
				Type paramType = ((ParameterizedType) type).getActualTypeArguments()[0];
				if (type instanceof WildcardType)
				{
					throw new RuntimeException("反序列化不能使用？泛型参数");
				}
				body = buildCollectionReader(targetClass, paramType);
			}
			else if (Map.class.isAssignableFrom(targetClass))
			{
				Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
				Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
				if (keyType instanceof WildcardType || valueType instanceof WildcardType)
				{
					throw new RuntimeException("反序列化不能使用？泛型参数");
				}
				body = buildMapReader(targetClass, keyType, valueType);
			}
			return buildClass(body, targetClass, strategy);
		}
		else if (type instanceof GenericArrayType)
		{
			Type root = type;
			while (root instanceof GenericArrayType)
			{
				root = ((GenericArrayType) root).getGenericComponentType();
			}
			if (root instanceof WildcardType)
			{
				throw new RuntimeException("反序列化需要确定的信息，不能使用？泛型参数");
			}
			body = buildArrayReader(type);
			if (root instanceof ParameterizedType)
			{
				root = ((ParameterizedType) root).getRawType();
			}
			return buildClass(body, (Class<?>) root, strategy);
		}
		else if (type instanceof WildcardType)
		{
			throw new RuntimeException("反序列化需要确定的信息，不能使用？泛型参数");
		}
		else
		{
			throw new RuntimeException("未知情况");
		}
	}
	
	private static Class<?> buildClass(String body, Class<?> targetClass, ReadStrategy strategy)
	{
		CompilerModel compilerModel = new CompilerModel("JsonReader_reader_" + count.getAndIncrement(), Object.class, JsonReader.class);
		try
		{
			if (strategy != null)
			{
				createStrategyConstructor(compilerModel);
			}
			MethodModel methodModel = new MethodModel(JsonReader.class.getMethods()[0]);
			methodModel.setBody(body);
			compilerModel.putMethod(JsonReader.class.getMethods()[0], methodModel);
			compilerModel.addImport(JsonArray.class, JsonObject.class, ReaderContext.class, ReadStrategy.class, Strategy.class, Iterator.class);
			logger.trace("为目标类{}生成的读取json的源码是\n{}\r\n", targetClass.getName(), compilerModel.toStringWithLineNo());
			JavaStringCompiler compiler = new JavaStringCompiler();
			return compiler.compile(compilerModel, targetClass.getClassLoader());
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}
	
	private static void createStrategyConstructor(CompilerModel model)
	{
		model.addField(new FieldModel("readStrategy", ReadStrategy.class));
		model.addConstructor("this.readStrategy = $0;", ReadStrategy.class);
	}
	
	private static String buildArrayReader(Type targetClass)
	{
		String rootName = null;
		int dim = 0;
		if (targetClass instanceof Class)
		{
			Class<?> rootType = (Class<?>) targetClass;
			dim = 0;
			while (rootType.isArray())
			{
				dim++;
				rootType = rootType.getComponentType();
			}
			rootName = rootType.getName();
		}
		else
		{
			Type root = targetClass;
			dim = 0;
			while (root instanceof GenericArrayType)
			{
				dim++;
				root = ((GenericArrayType) root).getGenericComponentType();
			}
			if (root instanceof ParameterizedType)
			{
				root = ((ParameterizedType) root).getRawType();
				if (root instanceof Class)
				{
					rootName = ((Class<?>) root).getName();
				}
				else
				{
					throw new RuntimeException("反序列化需要的类定义信息不明确");
				}
			}
			else
			{
				rootName = ((Class<?>) root).getName();
			}
		}
		String str = "{\r\n\tJsonArray jsonArray" + dim + " = ((JsonArray)$1);\r\n";
		str += "\tint l" + dim + " = jsonArray" + dim + ".size();\r\n";
		str += "\t" + NameTool.buildDimTypeName(rootName, dim) + " array" + dim + " = " + NameTool.buildNewDimTypeName(rootName, dim, "l" + dim) + ";\r\n";
		str += "\t" + "for(int i" + dim + " = 0;i" + dim + "<l" + dim + ";i" + dim + "++)\r\n";
		str += "\t{\r\n";
		String bk = "\t\t";
		for (int i = dim - 1; i > 0; i--)
		{
			str += bk + "JsonArray jsonArray" + i + " =jsonArray" + (i + 1) + ".getJsonArray(i" + (i + 1) + ");\r\n";
			str += bk + "int l" + i + " = jsonArray" + i + ".size();\r\n";
			str += bk + NameTool.buildDimTypeName(rootName, i) + " array" + i + " = " + NameTool.buildNewDimTypeName(rootName, i, "l" + i) + ";\r\n";
			str += bk + "for(int i" + i + " = 0;i" + i + "<l" + i + ";i" + i + "++)\r\n";
			str += bk + "{\r\n";
			bk += "\t";
		}
		if (targetClass instanceof Class)
		{
			str += bk + "array1[i1] = (" + rootName + ")ReaderContext.read(" + rootName + ".class,jsonArray1.get(i1));\r\n";
		}
		else
		{
			throw new RuntimeException("javassist不支持泛型参数，无法完成该对象的");
			// str += bk + "Type type = new TypeUtil<" +
			// String.valueOf(component) + ">(){}.getType();\r\n";
			// str += bk + "array1[i1] = (" + rootName +
			// ")ReaderContext.read(type,jsonArray1.get(i1));\r\n";
		}
		for (int i = 1; i <= dim - 1; i++)
		{
			bk = bk.substring(0, bk.length() - 1);
			str += bk + "}\r\n";
			str += bk + "array" + (i + 1) + "[i" + (i + 1) + "] = array" + i + ";\r\n";
		}
		str += "\t}\r\n";
		str += "\treturn array" + dim + ";\r\n";
		str += "}";
		return str;
		
	}
	
	private static String buildCollectionReader(Class<?> collectionClass, Type paramType)
	{
		String str = "{\r\n\t" + collectionClass.getName() + " collection = new " + collectionClass.getName() + "();\r\n";
		str += "\tJsonArray jsonArray = (JsonArray)$1;\r\n";
		str += "\tint size = jsonArray.size();\r\n";
		str += "\tfor(int i=0;i<size;i++)\r\n";
		str += "\t{\r\n";
		if (wrapperSet.contains(paramType))
		{
			str += "\t\tcollection.add(jsonArray.getW" + ((Class<?>) paramType).getSimpleName() + "(i));\r\n";
		}
		else if (paramType instanceof Class)
		{
			str += "\t\tcollection.add(ReaderContext.read(" + ((Class<?>) paramType).getName() + ".class,jsonArray.get(i)));\r\n";
		}
		else if (paramType instanceof Type)
		{
			str += "\t\tcollection.add(ReaderContext.read(new TypeUtil<" + String.valueOf(paramType) + ">(){}.getType(),jsonArray.get(i));\r\n";
		}
		else
		{
			throw new RuntimeException("未知错误");
		}
		str += "\t}\r\n";
		str += "\treturn collection;\r\n";
		str += "}";
		return str;
	}
	
	private static String buildMapReader(Class<?> mapClass, Type keyType, Type valueType)
	{
		String str = "{\r\n\t" + mapClass.getName() + " map = new " + mapClass.getName() + "();\r\n";
		str += "\tJsonObject jsonObject = (JsonObject)$1;\r\n";
		str += "\tint size = jsonObject.size();\r\n";
		str += "\tIterator it = jsonObject.entrySet().iterator();\r\n";
		str += "\tObject key = null;\r\n";
		str += "\tObject value = null;\r\n";
		str += "\twhile(it.hasNext())\r\n";
		str += "\t{\r\n";
		str += "\t\tjava.util.Map.Entry each = (java.util.Map.Entry)it.next();\r\n";
		if (keyType instanceof Class)
		{
			if (((Class<?>) keyType).equals(String.class))
			{
				str += "\t\tkey = (String)each.getKey();\r\n";
			}
			else if (((Class<?>) keyType).equals(Character.class))
			{
				str += "\t\tkey = ((String)each.getKey()).charAt(0);\r\n";
			}
			else if (wrapperSet.contains(keyType))
			{
				str += "\t\tkey = " + ((Class<?>) keyType).getName() + ".valueOf((String)each.getKey());\r\n";
			}
			else
			{
				str += "\t\tkey = ReaderContext.read(" + ((Class<?>) keyType).getName() + ".class,(String)each.getKey());\r\n";
			}
		}
		else
		{
			throw new RuntimeException("暂时超出处理逻辑，请发邮件给作者eric@jfire.cn");
		}
		if (valueType instanceof Class)
		{
			Class<?> tmp = (Class<?>) valueType;
			if (tmp.equals(String.class))
			{
				str += "\t\tvalue = (String)each.getValue();\r\n";
			}
			else if (tmp.equals(Character.class))
			{
				str += "\t\tvalue = ((String)each.getValue()).charAt(0);\r\n";
			}
			else if (wrapperSet.contains(tmp))
			{
				str += "\t\tvalue = jsonObject.getW" + tmp.getSimpleName() + "(each.getKey());\r\n";
			}
			else
			{
				str += "\t\tvalue = ReaderContext.read(" + tmp.getName() + ".class,each.getValue());\r\n";
			}
		}
		else
		{
			str += "\t\tvalue = ReaderContext.read(new TypeUtil<" + String.valueOf(valueType) + ">(){}.getType(),each.getValue());\r\n";
		}
		str += "\t\tmap.put(key,value);\r\n";
		str += "\t}\r\n";
		str += "\treturn map;";
		str += "}";
		return str;
	}
	
	public static void putReader(Class<?> ckass, JsonReader jsonReader)
	{
		readerMap.put(ckass, jsonReader);
	}
	
	private static boolean needIgnore(Method method, ReadStrategy strategy)
	{
		String fieldName = ReflectUtil.getFieldNameFromMethod(method);
		if (method.isAnnotationPresent(JsonIgnore.class) && method.getAnnotation(JsonIgnore.class).force())
		{
			return true;
		}
		if (strategy != null)
		{
			if (strategy.ignore(method.getDeclaringClass().getName() + '.' + fieldName))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		if (method.isAnnotationPresent(JsonIgnore.class))
		{
			return true;
		}
		try
		{
			Field field = method.getDeclaringClass().getDeclaredField(fieldName);
			if (field.isAnnotationPresent(JsonIgnore.class))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		catch (Exception e)
		{
			return false;
		}
	}
}
